#include "connector.h"
#include "../gui/wait.h"
#include "../data/account.h"
#include "../data/company.h"
#include "../data/evaluation.h"

#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QtNetwork/QtNetwork>
#include <QDebug>

Connector::Connector(QObject *parent) : QObject(parent) {
    qDebug()<<"Connector::Connector called.";
    dialogWait=new Wait((QWidget*)parent);
    qDebug()<<"Connector::Connector returned.";
}

Connector::~Connector() {
    qDebug()<<"Connector::~Connector called.";
    delete dialogWait;
    qDebug()<<"Connector::~Connector returned.";
}

State Connector::call(const QString &route, const QByteArray *input,
                      QByteArray *output) {
    qDebug()<<"Connector::call called.";
    State state;
    /* TODO: [Done] 发送POST request和接收reply
     * 使用QNetworkRequest、QNetworkManager等,发送data到网站，
     */
    QNetworkRequest netRequest;

    netRequest.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
    netRequest.setUrl(QUrl("http://127.0.0.1:5000/"+route));
    QNetworkAccessManager *netManager=new QNetworkAccessManager(this);
    QNetworkReply *netReply=netManager->post(netRequest,*input);
    dialogWait->show();
    /* （承上）
     * 使用QEventLoop进行阻塞，以等待网站返回数据。
     * 将受到的数据存入output。
     * 视情况修改state的值。
     */
    QEventLoop netLoop;
    QTimer timer;
    timer.setInterval(5000);
    if(route=="train")
        timer.setInterval(150000);
    timer.setSingleShot(true);
    connect(&timer, &QTimer::timeout, &netLoop, &QEventLoop::quit);
    connect(netReply,&QNetworkReply::finished,&netLoop,&QEventLoop::quit);
    timer.start();
    netLoop.exec();
    QVariant statusCode=netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    if(timer.isActive()) {
        timer.stop();
        qDebug()<<statusCode.toInt();
    } else {
        qDebug()<<"Timeout";
    }
    if(statusCode.toInt()>=200&&statusCode.toInt()<=206) {
        output->append(netReply->readAll());
        state=State::Succeeded;
    } else if (statusCode.toInt()==408)
        state=State::Timeout;
    else
        state=State::Failed;
    dialogWait->hide();
    qDebug()<<"Connector::call returned.";
    return state;
}

/* TODO: [Done] JSON数据转换
 * 实现以下函数：login、enroll、query、filter、predict、train。
 * 将输入转换成JSON格式，作为参数调用call，
 * 将call的输出转换成JSON对象，并进一步转换成函数的输出。
 *
 * 如果call函数暂时无法使用，那么注释掉调用call的语句，自己编造数据作为输出。
 */

//QByteArray to QJsonObject
QJsonObject getJsonObjectFromByteArray(const QByteArray jsonString) {
    qDebug()<<"getJsonObjectFromByteArray called.";
    QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonString);
    if( jsonDocument.isNull() ) {
        qDebug()<< "===> QJsonDocument："<< jsonString;
    }
    QJsonObject jsonObject = jsonDocument.object();
    qDebug()<<"getJsonObjectFromByteArray returned.";
    return jsonObject;
}

State Connector::login(const QString *username, const QString *password,
                       QString *state) {
    qDebug()<<"Connector::login called.";
#ifdef OFFLINE
    *state="Admin";
    qDebug()<<"Connector::login returned.";
    return State::Succeeded;
#endif

    QJsonArray loginArray;
    loginArray.append(*username);
    loginArray.append(*password);

    QByteArray *input = new QByteArray();
    *input=QJsonDocument(loginArray).toJson();
    QByteArray *output= new QByteArray();

    State s=call("login",input,output);
    if(s!=State::Succeeded) return s;

    QJsonArray outArray;
    outArray=QJsonDocument::fromJson(*output).array();

    *state = output[0];
    qDebug()<<"Connector::login returned.";
    return s;
}

State Connector::enroll(const QString *username, const QString *password,
                        QString *state) {
    qDebug()<<"Connector::enroll called.";
#ifdef OFFLINE
    *state="Success";
    return State::Succeeded;
#endif

    QJsonArray enrollArray;
    enrollArray.append(*username);
    enrollArray.append(*password);

    QByteArray *input = new QByteArray();
    *input=QJsonDocument(enrollArray).toJson();
    QByteArray *output= new QByteArray();

    State s=call("register",input,output);
    if(s!=State::Succeeded) return s;

    *state=output[0];

    qDebug()<<"Connector::enroll returned.";
    return s;
}

State Connector::query(const QStringList *entname,int pageSize,int pageRequest,
                       int *pageCurr,int *pageTotal,
                       QList<Company *> *companyList) {
    qDebug()<<"Connector::query called.";
    QJsonArray entnameArray;
    for (int i = 0; i < entname->length(); i++) {
        entnameArray.append(entname->value(i));
    }

    QJsonObject queryJson;
    queryJson.insert("sortby","企业名称");
    queryJson.insert("page", pageRequest);
    queryJson.insert("amount", pageSize);
    queryJson.insert("entname", entnameArray);

    QByteArray *input = new QByteArray();
    *input=QJsonDocument(queryJson).toJson();
    QByteArray *output= new QByteArray();

    State s=call("query",input,output);
    if(s!=State::Succeeded) return s;

    QJsonObject queryOut;
    queryOut=QJsonDocument::fromJson(*output).object();
    QJsonArray dataA;
    QJsonArray sourceA;
    dataA=queryOut["data"].toArray();
    sourceA=queryOut["source"].toArray();
    int num=dataA.size();

    companyList->clear();
    for(int i = 0; i < num; i++) {
        QJsonArray data;
        QJsonArray source;

        data=dataA[i].toArray();
        companyList->append(new Company);
        companyList->back()->entname=new QString(data.at(1).toString());

        //给QStringList tag 赋值
        companyList->back()->tag=new QStringList;
        for(int j = 0; j < data.size(); j++) {
            companyList->value(i)->tag->append(data.at(2+j).toString());
        }

        source=sourceA[i].toArray();

        //给QList source 赋值
        companyList->back()->source=new QList<int>;
        for(int j = 0; j < source.size(); j++) {
            companyList->value(i)->source->append(source.at(2+j).toDouble());
        }
    }
    *pageCurr=queryOut["current"].toInt();
    *pageTotal=queryOut["total"].toInt();

    qDebug()<<"Connector::query returned.";
    return s;
}

State Connector::filter(const QVariantMap *constraint,int pageSize,int pageRequest,
                        int *pageCurr,int *pageTotal,
                        QList<Company *> *companyList) {
    qDebug()<<"Connector::filter called.";
    // QVariantMap 转 QJsonObject
    QJsonObject constraintObj;
    constraintObj=QJsonDocument::fromJson(QJsonDocument::fromVariant(QVariant(*constraint)).toJson()).object();

    // 转 Json
    QJsonObject filterJson;
    filterJson.insert("sortby","企业名称");
    filterJson.insert("page", pageRequest);
    filterJson.insert("amount", pageSize);
    filterJson.insert("constraint", constraintObj);

    QByteArray *input = new QByteArray();
    *input=QJsonDocument(filterJson).toJson();
    QByteArray *output= new QByteArray();

    State s=call("filter",input,output);
    if(s!=State::Succeeded) return s;

    QJsonObject filterOut;
    filterOut=QJsonDocument::fromJson(*output).object();
    QJsonArray dataA;
    QJsonArray sourceA;
    dataA=filterOut["data"].toArray();
    int num=dataA.size();
    sourceA=filterOut["source"].toArray();

    companyList->clear();
    for(int i = 0; i < num; i++) {
        QJsonArray data;
        QJsonArray source;

        data=dataA[i].toArray();
        companyList->append(new Company);
        companyList->back()->entname=new QString(data.at(1).toString());

        //给QStringList tag 赋值
        companyList->back()->tag=new QStringList;
        for(int j = 0; j < data.size(); j++) {
            companyList->value(i)->tag->append(data.at(2+j).toString());
        }

        source=sourceA[i].toArray();

        //给QList source 赋值
        companyList->back()->source=new QList<int>;
        for(int j = 0; j < source.size(); j++) {
            companyList->value(i)->source->append(source.at(2+j).toDouble());
        }
    }
    *pageCurr=filterOut["current"].toInt();
    *pageTotal=filterOut["total"].toInt();

    qDebug()<<"Connector::filter returned.";
    return s;
}

State Connector::predict(const Company *input,
                         Company *output) {
    qDebug()<<"Connector::predict called.";
    // QVariantMap 转 QJsonObject
    QJsonObject predictJson;
    predictJson=QJsonDocument::fromJson(QJsonDocument::fromVariant(QVariant(*input->data)).toJson()).object();

    QByteArray *predictIn = new QByteArray();
    *predictIn=QJsonDocument(predictJson).toJson();
    QByteArray *predictOut= new QByteArray();

    State s=call("predict",predictIn,predictOut);
    if(s!=State::Succeeded) return s;

    QJsonArray OutArray;
    OutArray=QJsonDocument::fromJson(*predictOut).array();
    output->tag->clear();
    for (int i = 0; i < OutArray.size(); i++) {
        output->tag->append(OutArray.at(i).toString());
    }

    qDebug()<<"Connector::predict returned.";
    return s;
}

State Connector::train(Evaluation *evaluation) {
    qDebug()<<"Connector::train called.";

    QByteArray *input= new QByteArray("{}");
    QByteArray *output= new QByteArray();

    State s=call("train",input,output);
    if(s!=State::Succeeded) return s;

    QJsonObject trainOut;
    trainOut=QJsonDocument::fromJson(*output).object();

    evaluation->score->resize(trainOut.size());
    for(int i=0; i<evaluation->target->size(); i++) {
        const QJsonArray &arr=trainOut[evaluation->target->value(i)].toArray();
        (*evaluation->score)[i].resize(arr.size());
        for(int j=0; j<evaluation->method->size(); j++)
            (*evaluation->score)[i][j]=arr[j].toDouble();
    }

    qDebug()<<"Connector::train returned.";
    return s;
}
